using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Sirenix.OdinInspector;
using UnityEditor;
using UnityEngine;


/// <summary>
/// 动态节点
/// </summary>
[Serializable, InlineEditor]
public class DynamicNode : ScriptableObject
{
    [LabelText("节点名称")]
    public string nodeName;
    
    [LabelText("节点中文名")]
    public string ZHName;
    
    /// <summary>
    /// 节点名称
    /// </summary>
    public string Name
    {
        get => nodeName;
        set => nodeName = value;
    }

    [InfoBox("节点类型未调整", InfoMessageType.Warning, "nodeIdChanged")]
    /// <summary>
    /// 节点 Type，替代 NodeType
    /// </summary>
    [LabelText("节点类型")]
    public int NodeId;
    private bool nodeIdChanged => NodeId == 0;

    /// <summary>
    /// 节点颜色
    /// </summary>
    [LabelText("节点颜色")]
    public Color NodeColor = Color.white;
    
    /// <summary>
    /// 是否含有重名属性
    /// </summary>
    private bool hasSameName;
    [InfoBox("重复属性名称", InfoMessageType.Error, "hasSameName")]
    
    public List<NodeProperty> props;

    public DynamicNode()
    {
        props = new List<NodeProperty>();
    }
    
    /// <summary>
    ///  生成
    /// </summary>
    [Button("生成", ButtonSizes.Large), GUIColor(0.7f, 0.6f, 0.7f)]
    public void Generate()
    {
        if (props == null || props.Count <= 0 || string.IsNullOrEmpty(nodeName))
            return;
        
        hasSameName = false;
        
        // Check same name
        for (int i = 0; i < props.Count; i++)
        {
            var p1 = props[i];
            for (int j = i + 1; j < props.Count; j++)
            {
                var p2 = props[j];
                if (p1.PropName.Equals(p2.PropName))
                {
                    hasSameName = true;
                    return;
                }
            }
            // node class name == prop name
            if (nodeName.Equals(p1.Type.ToString()))
            {
                Debug.LogError("[DynamicNode] 属性名称与 Class 名称冲突");
                hasSameName = true;
                return;
            }
        }
        
        Debug.Log("生成 节点脚本");
        
        ZHName = !string.IsNullOrEmpty(ZHName) ? ZHName : nodeName;
        var filePath = NodeMethodUtil.Delete(CreateNewNode.defaultPath, nodeName + ".cs");
        using (var sw = new StreamWriter (File.Open(filePath, FileMode.CreateNew), Encoding.UTF8))
        {        
            var scriptTxt = new StringBuilder("using System.Collections;\n");
            scriptTxt.Append("using System.Collections.Generic;\n");
            scriptTxt.Append("using BearUtil.Hidden;\n");
            scriptTxt.Append("using Sirenix.OdinInspector;\n");
            scriptTxt.Append("using UnityEngine;\n");

            scriptTxt.Append("namespace BearUtil\n");
            scriptTxt.Append("{\n");
            if (NodeColor == Color.white)
                scriptTxt.Append($"\t[CreateNodeMenu(\"自定义节点/{ZHName}\"), Title(\"{ZHName}\")]\n");
            else
                scriptTxt.Append($"\t[CreateNodeMenu(\"自定义节点/{ZHName}\"), Title(\"{ZHName}\"), NodeTintAttribute(\"#{ColorUtility.ToHtmlStringRGB(NodeColor)}\")]\n");
            scriptTxt.Append($"\tpublic class {nodeName} : BearNode\n");
            scriptTxt.Append("\t{\n");
            
            scriptTxt.Append("\n\t\tpublic override int type {");
            scriptTxt.Append($" get => {NodeId};");
            scriptTxt.Append("}\n");
            
            // fill node property
            for (int i = 0; i < props.Count; i++)
            {
                var prop = props[i];

                scriptTxt.Append(assignProp(prop));
            }

            scriptTxt.Append("\t}\n");
            scriptTxt.Append("}\n");

            sw.Write(scriptTxt.ToString());
            sw.Close();
        }
        
        CreateNewNode.Instance.UpdateFormat();
        AssetDatabase.Refresh();
    }

    private string assignProp(NodeProperty prop)
    {
        switch (prop.Type)
        {
            case NodePropertyType.Int:
                return string.Format("\t\t[LabelText(\"{0}\")]\n\t\tpublic int {1};\n", 
                    prop.Desc, 
                    prop.PropName);
            case NodePropertyType.ArrayInt:
                return  string.Format("\t\t[LabelText(\"{0}\")]\n\t\tpublic int[] {1};\n", 
                    prop.Desc,
                    prop.PropName);
            case NodePropertyType.String:
                return  string.Format("\t\t[LabelText(\"{0}\")]\n\t\tpublic string {1};\n", 
                    prop.Desc,
                    prop.PropName);
            case NodePropertyType.ArrayString:
                return  string.Format("\t\t[LabelText(\"{0}\")]\n\t\tpublic string[] {1};\n", 
                    prop.Desc,
                    prop.PropName);
            case NodePropertyType.Boolean:
                return  string.Format("\t\t[LabelText(\"{0}\")]\n\t\tpublic bool {1};\n", 
                    prop.Desc,
                    prop.PropName);
            case NodePropertyType.Float:
                return  string.Format("\t\t[LabelText(\"{0}\")]\n\t\tpublic float {1};\n", 
                    prop.Desc,
                    prop.PropName);
            case NodePropertyType.Vector2:
                return string.Format("\t\t[LabelText(\"{0}\")]\n\t\tpublic Vector2 {1};\n", 
                    prop.Desc,
                    prop.PropName);
            case NodePropertyType.Direction:
                return  string.Format("\t\t[LabelText(\"{0}\")]\n\t\tpublic ObjectDirection {1};\n", 
                    prop.Desc,
                    prop.PropName);
            case NodePropertyType.Node:
                return string.Format("\t\t[LabelText(\"{0}\")]\n\t\tpublic XNode.Node[] {1};\n", 
                    prop.Desc,
                    prop.PropName);
            case NodePropertyType.ActorID:
                return string.Format(
                           "\t\t[LabelText(\"{0}\"), ValueDropdown(\"getActorName\", DropdownWidth = 150), OnValueChanged(\"setRoleId\")]\n\t\tpublic string role_name;\n",
                           prop.Desc) +
                       $"\t\t[HideInInspector]\n\t\tpublic int {prop.PropName};\n"+
                       "\t\tprivate List<string> getActorName()\n" +
                       "\t\t{\n" +
                       "\t\t\treturn NodeMethodUtil.GetActorName();\n" +
                       "\t\t}\n" +
                       "\t\tprivate void setRoleId(string name)\n" +
                       "\t\t{\n" +
                       $"\t\t\t{prop.PropName} = NodeMethodUtil.FindRoleIDbyName(name);\n" +
                       "\t\t}\n";
            case NodePropertyType.AudioPlayData:
                return
                    "\t\t[HideLabel, ValueDropdown(\"GetAudioClip\", DropdownWidth = 300), OnValueChanged(\"SetValue\")]\n" +
                    "\t\tpublic AudioClip audio;\n" +
                    $"\t\t[LabelText(\"{prop.Desc}\"), HideInInspector]\n" +
                    $"\t\tpublic string {prop.PropName};\n" +
                    $"\t\tpublic void SetValue(AudioClip clip) => {prop.PropName} = clip.name;\n" +
                    "\t\tpublic List<AudioClip> GetAudioClip() { return AudioCtrl.Instance.Clips;}\n";
            default:
                // NodePropertyType.NewProperty
                var dp = CreateNewProperty.Instance.NewEnums.Find(e => e.Name == prop.Type.ToString());
                if (dp == null)
                    return "\t\t\n";
                else
                {
                    var valueType = dp.PType.ToString().ToLower();
                    var tmpValue = $"value_{dp.Name.ToLower()}";
                    // curtom property
                    return
                        $"\t\t[HideLabel, ValueDropdown(\"Get{dp.Name}\", DropdownWidth = 300), OnValueChanged(\"Set{dp.Name}\")]\n" +
                        $"\t\tpublic string _{tmpValue};\n" +
                        $"\t\t[LabelText(\"{prop.Desc}\"), HideInInspector]\n" +
                        $"\t\tpublic {valueType} {prop.PropName};\n" +
                        $"\t\tpublic void Set{dp.Name} (string propDesc) => {prop.PropName} = CreateNewProperty.Instance.GetPropertyContent(\"{dp.Name}\", _{tmpValue}).{dp.PType}Param;\n" +
                        $"\t\tpublic List<string> Get{dp.Name} () "+
                        "{ return CreateNewProperty.Instance.GetPropertyContentDesc(" +
                        $"\"{dp.Name}\"); " +
                        "}\n";
                }
        }
    }
}